本文章同時發佈於:
大家好,繼上次Week39 - 各種安全性演算法的應用 - 竊聽、電子欺騙實作之後,這次要介紹竄改
、抵賴
的實作。
以下實作大量參考Golang RSA encrypt and decrypt example與Golang: aes-256-cbc examples (with iv, blockSize)。
並且全部的範例都在此,請先 clone 下來會較好理解。
還記得Week38 - 「竄改(falsification)是什麼?」章節防範的方法嗎?就是
與電子欺騙的解決方法相同,傳輸人員在資料上產生一筆獨一無二的代碼供另一端驗證
接下來就要利用產生獨一無二的代碼
來實作,方法有 2 種:
小明
與早餐店阿姨
如果不夠信任彼此
,會採用數位簽章
,因為採用訊息識別碼
的話,小明
把鑰匙給早餐店阿姨
,早餐店阿姨
拿去冒名成小明
就不好了,
使用數位簽章
加在原本的循序圖串起來就會如下:
看看程式碼,進入week40/spoofing/signature
的資料夾:
$ cd week40/spoofing/signature
裡頭有以下檔案:
.
├── badGuyKey : 壞人的私鑰
├── badGuyKey.pub : 壞人的公鑰
├── goodGuykey : 小明的私鑰
├── goodGuykey.pub: 小明的公鑰
└── main.go : 程式碼
key
與key.pub
是透過ssh-keygen
這個軟體來產生的,使用以下指令可以產生一組RSA
公私鑰:
$ ssh-keygen -t rsa -f <key's name> -m pem
e.g.
$ ssh-keygen -t rsa -f goodGuykey -m pem
輸入後會詢問是否要設定passphrase
,這是一個安全密碼,如果設定了,以後使用此私鑰還要輸入此安全密碼才可使用,以增加安全性,此範例沒有設定。
code 的方面主要可以看main
的部分,註解有解釋流程,搭配循序圖會較好理解:
// 大量參考: https://gist.github.com/mfridman/c0c5ece512f63d429c4589196a1d4242
package main
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha512"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"log"
)
// LoadFile load the file to bytes
func LoadFile(path string) []byte {
content, err := ioutil.ReadFile(path)
if err != nil {
log.Fatal(err)
}
return content
}
// BytesToPrivateKey bytes to private key
func BytesToPrivateKey(priv []byte) *rsa.PrivateKey {
block, _ := pem.Decode(priv)
enc := x509.IsEncryptedPEMBlock(block)
b := block.Bytes
var err error
if enc {
log.Println("is encrypted pem block")
b, err = x509.DecryptPEMBlock(block, nil)
if err != nil {
log.Fatal(err)
}
}
key, err := x509.ParsePKCS1PrivateKey(b)
if err != nil {
log.Fatal(err)
}
return key
}
func main() {
// 壞人的私鑰
badGuyPrivateKey := BytesToPrivateKey(LoadFile("./badGuyKey"))
// 小明的私鑰
goodGuyPrivateKey := BytesToPrivateKey(LoadFile("./goodGuyKey"))
// 小明的公鑰,公鑰可以透過私要來取得,所以這邊就不在載入公鑰檔案了
goodGuyPublicKey := goodGuyPrivateKey.PublicKey
// 小明用自己的私鑰對訊息簽章
messageBytes := []byte("小明餐點: 大冰奶")
hash := sha512.New()
hash.Write(messageBytes)
hashed := hash.Sum(nil)
// 小明用自己的私鑰簽名
signature, err := rsa.SignPKCS1v15(rand.Reader, goodGuyPrivateKey, crypto.SHA512, hashed)
if err != nil {
panic(err)
}
// 小明的資料被壞人攔截,壞人開始偽造小明的訊息
messageBytes = []byte("小明餐點: 大冰紅")
hash = sha512.New()
hash.Write(messageBytes)
hashed = hash.Sum(nil)
// 壞人用自己的私鑰簽名,並非小明的
signature, err = rsa.SignPKCS1v15(rand.Reader, badGuyPrivateKey, crypto.SHA512, hashed)
if err != nil {
panic(err)
}
// 早餐店阿姨取得小明的公鑰,利用此公鑰驗證之後發現不是小明傳的訊息
err = rsa.VerifyPKCS1v15(&goodGuyPublicKey, crypto.SHA512, hashed, signature)
if err != nil {
fmt.Println("Two signatures are not the same. Error: ", err)
return
}
}
小明
與早餐店阿姨
如果夠信任彼此
,甚至他們可能是同一個系統,那就不必擔心早餐店阿姨
拿小明
的鑰匙做壞事了,故可採用訊息識別碼
,
使用訊息識別碼
加在原本的循序圖串起來就會如下:
看看程式碼,進入week40/falsification/HMAC
的資料夾:
$ cd week40/falsification/HMAC
裡頭有以下檔案:
.
└── main.go : 程式碼
code 主要可以看main
的部分,註解有解釋流程,搭配循序圖會較好理解:
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
)
func hmacSha256(data string, secret string) string {
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}
func main() {
sharedSecret := "小明與早餐店阿姨的共同鑰匙"
badGuySecret := "壞人的鑰匙"
meals := "小明餐點: 大冰紅"
// 小明利用與早餐店阿姨共同的鑰匙產生HMAC
requestHMAC := hmacSha256(meals, sharedSecret)
// 壞人攔截小明的封包,利用自己的鑰匙產生HMAC
requestHMAC := hmacSha256(meals, badGuySecret)
// 早餐店阿姨利用與小明共同的鑰匙產生HMAC
trueHMAC := hmacSha256(meals, sharedSecret)
// 早餐店阿姨比對此兩個HMAC,發現不同,故此訊息不是小明傳送的
if requestHMAC != trueHMAC {
fmt.Println("Two HMACs are not the same.")
return
}
}
還記得上篇文章 - 「抵賴(repudiation)是什麼?」章節防範的方法嗎?就是
要求傳輸人員在資料上產生一筆獨一無二的代碼供另一端驗證
接下來就要利用產生獨一無二的代碼
來實作,由於在抵賴
的情境中,小明
就是壞人,所以早餐店阿姨
與小明
本來就不夠信任彼此
,所以只可用 1 種方法:
使用數位簽章
加在原本的循序圖串起來就會如下:
看看程式碼,進入week40/repudiation/signature
的資料夾:
$ cd week40/repudiation/signature
裡頭有以下檔案:
.
├── goodGuyKey
├── goodGuyKey.pub
├── key
├── key.pub
└── main.go
鑰匙都是透過ssh-keygen
產生,可以參考上方竄改(falsification)
章節的講解,就不贅述。
code 主要可以看main
的部分,註解有解釋流程,搭配循序圖會較好理解:
// 大量參考: https://gist.github.com/mfridman/c0c5ece512f63d429c4589196a1d4242
package main
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha512"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"log"
)
// LoadFile load the file to bytes
func LoadFile(path string) []byte {
content, err := ioutil.ReadFile(path)
if err != nil {
log.Fatal(err)
}
return content
}
// BytesToPrivateKey bytes to private key
func BytesToPrivateKey(priv []byte) *rsa.PrivateKey {
block, _ := pem.Decode(priv)
enc := x509.IsEncryptedPEMBlock(block)
b := block.Bytes
var err error
if enc {
log.Println("is encrypted pem block")
b, err = x509.DecryptPEMBlock(block, nil)
if err != nil {
log.Fatal(err)
}
}
key, err := x509.ParsePKCS1PrivateKey(b)
if err != nil {
log.Fatal(err)
}
return key
}
func main() {
goodGuyPrivateKey := BytesToPrivateKey(LoadFile("./goodGuyKey"))
// 小明的公鑰,公鑰可以透過私要來取得,所以這邊就不在載入公鑰檔案了
goodGuyPublicKey := goodGuyPrivateKey.PublicKey
// 小明用自己的私鑰對訊息簽章
messageBytes := []byte("小明餐點: 大冰奶")
hash := sha512.New()
hash.Write(messageBytes)
hashed := hash.Sum(nil)
// 小明用自己的私鑰簽名
signature, err := rsa.SignPKCS1v15(rand.Reader, goodGuyPrivateKey, crypto.SHA512, hashed)
if err != nil {
panic(err)
}
// 早餐店阿姨取得小明的公鑰,驗證後發現的確是小明,傳送餐點回去給小明
err = rsa.VerifyPKCS1v15(&goodGuyPublicKey, crypto.SHA512, hashed, signature)
if err != nil {
fmt.Println("Two signatures are not the same. Error: ", err)
return
}
fmt.Println("Verify the signature is correct")
// 小明獲得餐點,並且吃完後開市賴帳
// 早餐店阿姨說明當初`rsa.VerifyPKCS1v15`利用小明的公鑰驗證後的確是小明用私鑰簽章的,故證明小明確實有點過餐
}